home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’93 / Listen to your hack…beat / midistuff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-18  |  11.3 KB  |  620 lines  |  [TEXT/KAHL]

  1. /***
  2.  * sonicpatch.c
  3.  *
  4.  *            Make sounds from the debugger over midi
  5.  *
  6.  *        by Bernie Bernstein for and entirely during MacHack '93 (June 16-18)
  7.  *
  8.  *        thanks to Chris DiGiano for the basis for midi functions
  9.  *
  10.  *        thanks to all the MacHackers for help getting this to work
  11.  ***/
  12.  
  13. #ifndef THINK_C
  14. #include <Types.h>
  15. #include <Quickdraw.h>
  16. #include <Memory.h>
  17. #include <OSUtils.h>
  18. #include <Traps.h>
  19. #include <Files.h>
  20. #include <Resources.h>
  21. #include <Packages.h>
  22. #endif
  23.  
  24. #ifdef SONICDCMD
  25. #include "dcmd.h"
  26. #endif
  27.  
  28. #ifdef SONICINIT
  29. #include "utils.h"
  30. #endif
  31.  
  32. #include <MIDI.h>
  33.  
  34. #include "midistuff.h"
  35.  
  36.  
  37. SonicGlobs    gGlobals;
  38.  
  39. /* fake out the compiler to find the icon */
  40. /* used by the dcmd and the init */
  41. void    MidiIcon(void);
  42.  
  43.  
  44.  
  45. #ifdef SONICDCMD
  46.  
  47. /***
  48.  * CommandEntry
  49.  *
  50.  *        the entry point for the dcmd
  51.  ***/
  52. pascal void CommandEntry (dcmdBlock* paramPtr)
  53. {
  54.     Str255    param;
  55.  
  56.     switch (paramPtr->request)
  57.         {
  58.         case dcmdInit:
  59.             gGlobals.soundEnabled     = false;
  60.             gGlobals.manualPatch     = false;
  61.             gGlobals.outputRefNum     = 0;
  62.             gGlobals.gotIcon        = false;
  63.             LoadIcon();
  64.             break;
  65.  
  66.         case dcmdHelp:
  67.             dcmdDrawLine ("\pPlay [on | connect | off |");
  68.             dcmdDrawLine ("\p      { note | stop } [channel [pitch [velocity]]]] |");
  69.             dcmdDrawLine ("\p      setchan channel instrument]");
  70.             dcmdDrawLine ("\p   Tell midi to play something");
  71.             break;
  72.  
  73.         case dcmdDoIt:
  74.             dcmdGetNextParameter(param);
  75.             
  76.             if (!gGlobals.gotIcon)
  77.                 {
  78.                 dcmdDrawLine("\pCan't do midi, icon didn't get loaded");
  79.                 return;
  80.                 }
  81.  
  82.             if (Pstrcmp(param, "\pon") == 0)
  83.                 {
  84.                 InitMidi();
  85.                 }
  86.             else if (Pstrcmp(param, "\pconnect") == 0)
  87.                 {
  88.                 MIDIManagerConnect();
  89.                 }
  90.             else if (Pstrcmp(param, "\poff") == 0)
  91.                 {
  92.                 KillMidi();
  93.                 }
  94.             else if (Pstrcmp(param, "\pnote") == 0)
  95.                 {
  96.                 long val;
  97.                 char channel, pitch, velocity;
  98.                 Boolean ok;
  99.  
  100.                 channel = 1;
  101.                 pitch = 64;
  102.                 velocity = 127;
  103.                 dcmdGetNextExpression(&val, &ok);
  104.                 if (ok)
  105.                     {
  106.                     channel = val;
  107.                     dcmdGetNextExpression(&val, &ok);
  108.                     if (ok)
  109.                         {
  110.                         pitch = val;
  111.                         dcmdGetNextExpression(&val, &ok);
  112.                         if (ok)
  113.                             {
  114.                             velocity = val;
  115.                             }
  116.                         }
  117.                     }
  118.                 StartNote(channel, pitch, velocity);
  119.                 }
  120.             else if (Pstrcmp(param, "\pstop") == 0)
  121.                 {
  122.                 long val;
  123.                 char channel, pitch;
  124.                 Boolean ok;
  125.  
  126.                 channel = 1;
  127.                 pitch = -1;
  128.                 dcmdGetNextExpression(&val, &ok);
  129.                 channel = val;
  130.                 if (ok)
  131.                     {
  132.                     dcmdGetNextExpression(&val, &ok);
  133.                     pitch = val;
  134.                     }
  135.                 StopNote(channel, pitch);
  136.                 }
  137.             else if (Pstrcmp(param, "\psetchan") == 0)
  138.                 {
  139.                 long val;
  140.                 char channel, instrument;
  141.                 Boolean ok;
  142.                 
  143.                 dcmdGetNextExpression(&val, &ok);
  144.                 channel = val;
  145.                 if (ok)
  146.                     {
  147.                     dcmdGetNextExpression(&val, &ok);
  148.                     instrument = val;
  149.                     }
  150.                 if (!ok)
  151.                     {
  152.                     dcmdDrawLine("\pNeed a channel and an instrument number.");
  153.                     dcmdDrawLine("\peg. Play setchan 1 10");
  154.                     return;
  155.                     }
  156.                 SetProgramChannel(channel, instrument);
  157.                 }
  158.             else
  159.                 {
  160.                 dcmdDrawLine ("\pNot implemented.");
  161.                 }
  162.             break;
  163.         }
  164. } // CommandEntry
  165.  
  166. #endif
  167.  
  168.  
  169.  
  170. /***
  171.  * LoadIcon
  172.  *
  173.  *        Load the icon from the fake code or the resource
  174.  ***/
  175. void LoadIcon( )
  176. {
  177. #if defined(SONICDCMD) || defined(SONICINIT)
  178.     gGlobals.iconHdl = NewHandleSys(kIconSize);
  179.     if (gGlobals.iconHdl)
  180.         {
  181.         BlockMove((Ptr)MidiIcon, *gGlobals.iconHdl, kIconSize);
  182.         gGlobals.gotIcon = true;
  183.         }
  184. #else
  185.     gGlobals.iconHdl = (Handle)GetResource('ICN#', kIconID);
  186.     if (gGlobals.iconHdl)
  187.         {
  188.         gGlobals.gotIcon = true;
  189.         }
  190.     else
  191.         {
  192.         DebugStr("\pCouldn't load icon");
  193.         }
  194. #endif
  195. }
  196.  
  197.  
  198. /***
  199.  * PreInitMidi
  200.  *
  201.  *        Just initialize variables
  202.  ***/
  203. void PreInitMidi()
  204. {
  205.     gGlobals.soundEnabled     = false;
  206.     gGlobals.manualPatch     = false;
  207.     gGlobals.outputRefNum     = 0;
  208.     gGlobals.gotIcon        = false;
  209.     gGlobals.iconHdl        = NULL;
  210.     LoadIcon();
  211. }
  212.  
  213.  
  214. /***
  215.  * InitMidi
  216.  *
  217.  *        Sign into the midi manager
  218.  ***/
  219. void InitMidi( )
  220. {
  221.     OSErr err;
  222.  
  223. #ifdef SONICAPP
  224.     PreInitMidi();
  225. #endif
  226.  
  227.     if (!gGlobals.soundEnabled)
  228.         {
  229.  
  230.         err = MIDIManagerSignup( );
  231.         gGlobals.soundEnabled = (err == noErr);
  232. #ifdef SONICDCMD
  233.         if (!gGlobals.soundEnabled)
  234.             dcmdDrawLine ("\pCouldn't sign on to midi manager");
  235.         }
  236.     else
  237.         dcmdDrawLine("\pAlready signed on to midi");
  238. #elif defined(SONICINIT)
  239.         if (!gGlobals.soundEnabled)
  240.             DebugStr ("\pCouldn't sign on to midi manager");
  241.         }
  242. #else
  243.         }
  244. #endif
  245.  
  246. }
  247.  
  248.  
  249. /***
  250.  * KillMidi
  251.  *
  252.  *        Sign out from midi
  253.  ***/
  254. void KillMidi( )
  255. {
  256.     OSErr err;
  257.     err = SilenceAll();
  258.     MIDISignOut(gGlobals.clientID);
  259.     
  260. #ifdef SONICDCMD
  261.     dcmdDrawLine ("\pSigned out from midi manager");
  262. #endif
  263.  
  264.     gGlobals.soundEnabled = false;
  265. }
  266.  
  267.  
  268. /***
  269.  * MIDIManagerSignup
  270.  *
  271.  *        Sign into the midi manager (put the icon in the patch pay)
  272.  ***/
  273. OSErr MIDIManagerSignup( )
  274. {
  275.     MIDIPortParams    init;
  276.     OSErr            err;
  277.     long            MIDIMgrVerNum;
  278.     OSType            sdbClientID;
  279.     
  280.     /* see if the midi manager is installed and remember version number */
  281.     MIDIMgrVerNum = SndDispVersion( midiToolNum);
  282.     if (MIDIMgrVerNum == 0)
  283.         return errNoMIDIManager;
  284.     
  285.     /* sign into the manager */
  286.     /* by finding an unused name */
  287.     for (err = midiDupIDErr, sdbClientID = kStartSignature;
  288.             err == midiDupIDErr && (sdbClientID - kStartSignature) < kMaxClients;
  289.             sdbClientID++)
  290.         {
  291.         if ((err = MIDISignIn(sdbClientID, refCon0, gGlobals.iconHdl, "\pSonic Debug")) == noErr)
  292.         break;
  293.         }
  294.     if (err != noErr)
  295.         {
  296. #ifdef SONICINIT
  297.         DebugStr("\perror signing in");
  298. #endif
  299.         return err;
  300.         }
  301.     
  302.     gGlobals.clientID = sdbClientID;
  303.  
  304.     // Assume not a Patchbay configuration.
  305.     gGlobals.manualPatch = true;    
  306.  
  307.     // Add an output port.
  308.     init.portID = outputPortID;
  309.     init.portType = midiPortTypeOutput;
  310.     init.timeBase = 0;
  311.     init.offsetTime = midiGetCurrent;
  312.     init.readHook = NULL;
  313.     init.refCon = (long) &gGlobals;
  314.     Pstrcpy(init.name, "\pOutput");
  315.     err = MIDIAddPort(sdbClientID, outputPortBuffSize, &gGlobals.outputRefNum, &init);
  316.     
  317.     // Has a PatchBay connection been resolved?
  318.     if (err == midiVConnectMade)
  319.         {
  320.         gGlobals.manualPatch = false;
  321.         }
  322.     else if (err != noErr)
  323.         {
  324. //        MIDISignOut(sdbClientID);
  325. #ifdef SONICINIT
  326.         DebugStr("\pError adding the port");
  327. #endif
  328.         return err;
  329.         }
  330.  
  331.     return noErr;
  332. }
  333.  
  334.  
  335. /***
  336.  * MIDIManagerConnect
  337.  *
  338.  *        Connect this midi device to the player.
  339.  ***/
  340. OSErr MIDIManagerConnect( )
  341. {
  342.     OSErr err;
  343.  
  344.     if (gGlobals.manualPatch)
  345.         {
  346.         // ••• this doesnt work. I dont know the input port for 'amdr' Apple Midi Driver
  347.         err = MIDIConnectData( gGlobals.clientID, outputPortID, 'amdr', inputPortID);
  348.         if (err != noErr)
  349.             {
  350. #ifdef SONICDCMD
  351.             dcmdDrawLine("\pCouldn't connect to Midi Manager");
  352. #endif
  353.             return err;
  354.             }
  355.         }
  356.     return noErr;
  357. }
  358.  
  359.  
  360.  
  361. /***
  362.  * StartNote
  363.  *
  364.  *            Start playing a note to the given:
  365.  *             channel (1-16)
  366.  *             pitch (1-127)
  367.  *             velocity (loudness) (1-127)
  368.  ***/
  369. OSErr StartNote(char channel, char pitch, char velocity)
  370. {
  371.     OSErr err = noErr;
  372.  
  373.     if (gGlobals.soundEnabled)
  374.         {
  375.         MIDIPacket    output;
  376.         output.flags = midiMsgType + midiTimeStampCurrent + midiNoCont;
  377.         output.len = 9;
  378.         output.tStamp = 0;
  379.         
  380.         output.data[0] = 0x90 + channel - 1;    // 0x90 is Note On code
  381.         output.data[1] = pitch;
  382.         output.data[2] = velocity;
  383.             
  384.         err = MIDIWritePacket(gGlobals.outputRefNum, &output);
  385.         
  386.         }
  387.     return err;
  388. }
  389.  
  390.  
  391. /***
  392.  * StopNote
  393.  *
  394.  *            Start playing the note given:
  395.  *             channel (1-16)
  396.  *             pitch (0-127)
  397.  *
  398.  *            if pitch is zero, it stops all notes on the channel
  399.  ***/
  400. OSErr StopNote(char channel, char pitch)
  401. {
  402.     OSErr err = noErr;
  403.     if (gGlobals.soundEnabled)
  404.         {
  405.         MIDIPacket    output;
  406.         
  407.         if (pitch > 0)
  408.             {
  409.             output.flags = midiMsgType + midiTimeStampCurrent + midiNoCont;
  410.             output.len = 9;
  411.             output.tStamp = 0;
  412.             
  413.             output.data[0] = 0x80 + channel - 1;    // 0x80 is Note On code
  414.             output.data[1] = pitch;
  415.             output.data[2] = 0;
  416.                 
  417.             err = MIDIWritePacket(gGlobals.outputRefNum, &output);
  418.             }
  419.         else
  420.             {
  421.             output.flags = midiMsgType + midiTimeStampCurrent + midiNoCont;
  422.             output.len = 9;
  423.             output.tStamp = 0;
  424.             output.data[0] = 0xB0 + channel - 1;
  425.             output.data[1] = 120;        // all sound off code
  426.             output.data[2] = 0;
  427.                             
  428.             err = MIDIWritePacket(gGlobals.outputRefNum, &output);
  429.  
  430.             if (err == noErr)
  431.                 {
  432.                 output.flags = midiMsgType + midiTimeStampCurrent + midiNoCont;
  433.                 output.len = 9;
  434.                 output.tStamp = 0;
  435.                 output.data[0] = 0xB0 + channel - 1;
  436.                 output.data[1] = 123;        // all notes off code
  437.                 output.data[2] = 0;
  438.                 err = MIDIWritePacket(gGlobals.outputRefNum, &output);
  439.                 }
  440.             }
  441.         }
  442.     return err;
  443. }
  444.  
  445.  
  446. /***
  447.  * SetPanPot
  448.  *
  449.  *        Set the position (R/L) for the given channel (0-127)
  450.  ***/
  451. OSErr SetPanPot(char channel, char position)
  452. {
  453.     OSErr err = noErr;
  454.     if (gGlobals.soundEnabled)
  455.         {
  456.         MIDIPacket    output;
  457.         
  458.         output.flags = midiMsgType + midiTimeStampCurrent + midiNoCont;
  459.         output.len = 9;
  460.         output.tStamp = 0;
  461.         
  462.         output.data[0] = 0xB0 + channel - 1;
  463.         output.data[1] = 10;        // pan pot code
  464.         output.data[2] = position;
  465.             
  466.         err = MIDIWritePacket(gGlobals.outputRefNum, &output);
  467.         }
  468.     return err;
  469. }
  470.  
  471.  
  472. /***
  473.  * SetProgramChannel
  474.  *
  475.  *        Set the voice (instrument) for a given channel.
  476.  ***/
  477. OSErr SetProgramChannel(char channel, char voice)
  478. {
  479.     OSErr err = noErr;
  480.     if (gGlobals.soundEnabled)
  481.         {
  482.         MIDIPacket    output;
  483.         
  484.         output.flags = midiMsgType + midiTimeStampCurrent + midiNoCont;
  485.         output.len = 9;
  486.         output.tStamp = 0;
  487.         
  488.         output.data[0] = 0xB0 + channel - 1;
  489.         output.data[1] = 32;        // Bank Select LSB
  490.         output.data[2] = voice;
  491.             
  492.         err = MIDIWritePacket(gGlobals.outputRefNum, &output);
  493.         
  494.         if (err == noErr)
  495.             {
  496.             output.flags = midiMsgType + midiTimeStampCurrent + midiNoCont;
  497.             output.len = 9;
  498.             output.tStamp = 0;
  499.             
  500.             output.data[0] = 0xC0 + channel - 1;
  501.             output.data[1] = 0;    // The value of this doesn't seem to matter
  502.             output.data[2] = voice - 1;
  503.                 
  504.             err = MIDIWritePacket(gGlobals.outputRefNum, &output);
  505.             }
  506.         }
  507.     return err;
  508. }
  509.  
  510.  
  511. /***
  512.  * SilenceAll
  513.  *
  514.  *        Make all sounds stop on all channels
  515.  ***/
  516. OSErr SilenceAll()
  517. {
  518.     OSErr err = noErr;
  519.     if (gGlobals.soundEnabled)
  520.         {
  521.         short channel;
  522.         MIDIPacket    output;
  523.         // try and shut up each of 16 MIDI channels
  524.         
  525.         for (channel = 1; channel <= 16; channel++)
  526.             {
  527.             output.flags = midiMsgType + midiTimeStampCurrent + midiNoCont;
  528.             output.len = 9;
  529.             output.tStamp = 0;
  530.             output.data[0] = 0xB0 + channel - 1;
  531.             output.data[1] = 120;        // all sound off code
  532.             output.data[2] = 0;
  533.                             
  534.             err = MIDIWritePacket(gGlobals.outputRefNum, &output);
  535.  
  536.             if (err == noErr)
  537.                 {
  538.                 output.flags = midiMsgType + midiTimeStampCurrent + midiNoCont;
  539.                 output.len = 9;
  540.                 output.tStamp = 0;
  541.                 output.data[0] = 0xB0 + channel - 1;
  542.                 output.data[1] = 123;        // all notes off code
  543.                 output.data[2] = 0;
  544.                 err = MIDIWritePacket(gGlobals.outputRefNum, &output);
  545.                 }
  546.  
  547.             }
  548.         }
  549.  
  550.     return err;
  551. }        
  552.  
  553.  
  554.  
  555. /***
  556.  * Some standard Pascal string operations
  557.  *
  558.  *        borrowed from Sven Axelsson, GU (thanks)
  559.  ***/
  560.  
  561.  
  562. // concat s2 to s1
  563. char *
  564. Pstrcat(
  565.     register unsigned char    *s1,
  566.     register unsigned char    *s2 )
  567. {
  568.     register unsigned int    i;
  569.     unsigned int            l = s2[0];
  570.     char                    *s = (char *) s1;
  571.     
  572.     s1 = &s1[s1[0]];
  573.     for( i = 1; i <= l; i++ ) {
  574.         *++s1 = *++s2;
  575.     }
  576.     s[0] += --i;
  577.     
  578.     return( s );
  579. }
  580.  
  581.  
  582. // copy s2 into s1
  583. char *
  584. Pstrcpy(
  585.     register unsigned char    *s1,
  586.     register unsigned char    *s2 )
  587. {
  588.     register unsigned int    i;
  589.     unsigned int            l = s2[0];
  590.     char                    *s = (char *) s1;
  591.     
  592.     for( i = 0; i <= l; i++ ) {
  593.         *s1++ = *s2++;
  594.     }
  595.     
  596.     return( s );
  597. }
  598.  
  599.  
  600. // compare s1 and s2 (zero if equal)
  601. short
  602. Pstrcmp(
  603.     register Str255            s1,
  604.     register Str255            s2 )
  605. {
  606.     register unsigned int    i;
  607.     register short            cmp;
  608.     unsigned int            l = s1[0];
  609.     
  610.     for( i = 0; i <= l; i++ ) {
  611.         cmp = *s1++ - *s2++;
  612.         if( cmp ) {
  613.             return( cmp );
  614.         }
  615.     }
  616.     
  617.     return( 0 );
  618. }
  619.  
  620.